package cn.funjson.dicttransformer.model;

import com.alibaba.fastjson.JSONObject;
import cn.funjson.dicttransformer.hook.AbstractDataHook;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 对象映射node
 * @author funjson
 * @date 2021-08-09 22:25
 * @version v2.0
 */
@Data
@Slf4j
public class Node {

    /**
     * 保存上一个节点 头节点为空
     */
    private Node pre;

    /**
     * 保存下一个节点 尾节点为空
     */
    private Node next;

    /**
     * 保存当前对象属性名(当前对象在调用链上一层的属性名)
     */
    private String fieldName;

    /**
     * 保存当前value属性名(被转移后的数据被存放的属性名,这个是获取的注解上面的值)
     */
    private String valueName;

    /**
     * 保存当前key属性名(key/字典 属性名)
     */
    private String keyName;

    /**
     * 保存当前key对应的group值(业务字段)
     */
    private String group;

    /**
     * 保存当前class对象(保存当前node的class)
     */
    private Class<?> clazz;

    /**
     * 存放数据钩子
     */
    private AbstractDataHook dataHook;

    /**
     * 集合标记
     */
    private boolean flagList;

    private static boolean hasNext(Node node){
        return node.next != null;
    }

    private static boolean hasPre(Node node){
        return node.pre != null;
    }

    public static Node getBottom(Node node){
        while (hasNext(node)){
            node=getBottom(node.next);
        }
        return node;
    }

    public static Node getTop(Node node){
        while (hasPre(node)){
            node=getTop(node.pre);
        }
        return node;
    }

    /**
     * 反向解析调用链
     * @param node node
     */
    public static Object reverseAndUpdateField(List<?> objects,Node node,boolean flag) throws Throwable {

        // 判断是否来自集合类型 集合类型由于要进行递归解析 node是特殊处理过的 不需要返回top
        if (!flag){
            // 因为是反向解析所以这里是递归解析top节点
            node=getTop(node);
        }

        log.debug("[TopNode]:{}", JSONObject.toJSONString(node));

        for (Object object : objects){
            // 代表当前操作的对象
            Object target=object;

            // 这个东西有缓存最好每次用的时候重新取一下
            BeanInfo beanInfo;
            List<PropertyDescriptor> descriptors;

            // 用来判断集合类
            boolean listFlag = false;

            // 通过这次遍历 可以获取到叶子节点的对象
            while (hasNext(node)){

                String fieldName = node.fieldName;
                node=node.next;
                listFlag = node.flagList;

                // 重新取一下
                descriptors = getPropertyDescriptors(target);

                for (PropertyDescriptor descriptor : descriptors) {
                    if (descriptor.getName().equals(fieldName)) {
                        Method readMethod = descriptor.getReadMethod();
                        target = readMethod.invoke(target);
                        break;
                    }
                }

                // 判断是否是集合类型
                if(listFlag){
                    List<?> targetList =(List<?>) target;
                    log.debug("递归的list:{}",JSONObject.toJSONString(targetList));
                    log.debug("递归的node:{}",JSONObject.toJSONString(node));
                    reverseAndUpdateField(targetList,node,true);
                }

            }

            log.debug("此时的node:{}",JSONObject.toJSONString(node));
            log.debug("此时的target:{}",JSONObject.toJSONString(target));
            log.debug("listFlag:{}",listFlag);

            List<PropertyDescriptor> listDescriptors = getPropertyDescriptors(target);
            node.updateValue(node, listDescriptors, target);

            log.debug("success:{}",JSONObject.toJSONString(object));
        }

        return objects.get(0);
    }

    /**
     * 获取属性描述器集合
     * @param target object
     * @return List<PropertyDescriptor>
     * @throws IntrospectionException 属性描述器异常
     */
    private static List<PropertyDescriptor> getPropertyDescriptors(Object target) throws IntrospectionException {
        BeanInfo beanInfo;
        List<PropertyDescriptor> descriptors;
        beanInfo = Introspector.getBeanInfo(target.getClass());
        descriptors = Arrays.stream(beanInfo.getPropertyDescriptors()).filter(p -> {
            String name = p.getName();
            //过滤掉不需要修改的属性
            return !"serialVersionUID".equals(name) && !"class".equals(name);
        }).collect(Collectors.toList());
        return descriptors;
    }

    /**
     * 转换赋值
     * @param node node
     * @param descriptors 属性描述器
     * @param o object
     * @throws IllegalAccessException IllegalAccessException
     * @throws InvocationTargetException IllegalAccessException
     */
    private void updateValue(Node node, List<PropertyDescriptor> descriptors, Object o) throws IllegalAccessException, InvocationTargetException {
        Object transfer = null;
        for (PropertyDescriptor descriptor : descriptors) {
            String key = null;
            String groupId = null;
            //descriptor.getWriteMethod()方法对应set方法
            // 在当前类中获取key值
            if (descriptor.getName().equals(node.keyName)) {
                Method readMethod = descriptor.getReadMethod();
                key = String.valueOf(readMethod.invoke(o));
                groupId= node.group;
                log.debug("获取到的key:{} groupId:{}",key,groupId);
                transfer=node.dataHook.lazyInitLocalData(Integer.parseInt(groupId),String.valueOf(key));
                break;
            }
        }
        for (PropertyDescriptor descriptor : descriptors) {
            // 在当前类中获取value值
            if (descriptor.getName().equals(node.valueName)) {
                Method writeMethod = descriptor.getWriteMethod();
                // 根据key和group查询数据 并且赋值
                writeMethod.invoke(o,transfer);
                log.debug("修改了target值:{}",transfer);
                break;
            }
        }
    }

}
